#!/usr/bin/perl

# Check parameters
if (! (defined($ARGV[0]) && defined($ARGV[1]) && defined($ARGV[2]))) {
	print "Usage: greedy-genes <language> <corpus-size> <genes> [ <rounds> ]\n";
	exit(1);
}

# Parameters
my $lang = $ARGV[0] || "danish";
my $size = $ARGV[1] || 15;
my $ngenes = $ARGV[2] || 286;
my $rounds = $ARGV[3] || 10;
my $threads = 4;
my $genestring = "320000000000000000000000000000000000000000000000000000000000000000000000";

# Set number of genes, initial bit value, and direction (1=lowest bit
# first, -1 = highest bit first)
my $startbit = 67 * 4;
my $initial = 0;
my $direction = -1;
my $directionswitch = 1;
my $time = `date '+%Y%m%d-%H%M%S'`;
chomp($time);
my $name = "greedy-gene-$time";
my $id = 1000;

# Initialize genes
my $genes = [];
for (my $i = 0; $i < $ngenes; ++$i) {
	$genes->[$i] = $initial;
}
$genes = genesFromString($genestring)
	if (defined($genestring));

# Optimize
print "Starting greedy-gene (lang=$lang, size=$size, ngenes=$ngenes, rounds=$rounds, name=$name)\n";
optimize($genes);

# Optimize
sub optimize {
	my $genes = shift;
	my $score = score(geneString($genes));

	# Iterate over genes in sequence
	print geneString($genes) . " $score\n";
	my $improvement = 1;
	for (my $round = 1; $round <= $rounds && $improvement; ++ $round) {
		print "Round $round\n";
		$improvement = 0;
		my $bit = $direction > 0 ? 0 : $ngenes - 1;
		$bit = $startbit
			if ($round == 1);

		while ($bit >= 0 && $bit < $ngenes) {
			# Create gene pool for threads
			my $genepool = [];
			for (my $i = 0; $i < $threads; ++$i) {
				flip($genes, $bit + $i * $direction);
				my $genestring = geneString($genes);
				push @$genepool, $genestring;
				flip($genes, $bit + $i * $direction);
			}
			my $scores = multiscores($genepool);

			# Find first bit where flip leads to improvement
			for (my $i = 0; $i < $threads; ++$i) {
				print "new greedy: " . $genepool->[$i] . " " . $scores->[$i] . " (flip $bit from $score)\n";
				if ($scores->[$i] >= $score) {
					$score = $scores->[$i];
					$improvement = 1;
					flip($genes, $bit);
					print "new best greedy: " . geneString($genes) . " $score (flip $bit)\n";
					$bit += $direction;

					# Proceed from this bit (recalculating subsequent bit if necessary)
					last;
				}
				$bit += $direction;
			}
		}
	}
}

# Subroutines
sub flip {
	my $genes = shift;
	my $i = shift;
	$genes->[$i] = 1 - $genes->[$i];
}

# Generate bit string
sub geneString {
	my $genes = shift;
	my $bit0 = $ngenes - ($ngenes % 4);
	my $s = "";
	while ($bit0 >= 0) {
		my $digit = 0;
		$digit += 1 if ($genes->[$bit0] > 0);
		$digit += 2 if ($genes->[$bit0 + 1] > 0);
		$digit += 4 if ($genes->[$bit0 + 2] > 0);
		$digit += 8 if ($genes->[$bit0 + 3] > 0);
		$s .= chr($digit + ($digit < 10 ? 48 : 87));
        $bit0 -= 4;
    }
    return $s;
}

sub genesFromString {
	my $string = shift;
	my $genes = [];
	for (my $i = 0; $i < length($string); ++$i) {
		my $digit = ord(substr($string, $i, 1));
		$digit -= ($digit < 97 ? 48 : 87);
		my $bit0 = (length($string) - $i - 1) * 4;
		$genes->[$bit0 + 0] = $digit & 1;
		$genes->[$bit0 + 1] = $digit & 2;
		$genes->[$bit0 + 2] = $digit & 4;
		$genes->[$bit0 + 3] = $digit & 8;
	}
	return $genes;
}

# Calculate scores
sub score {
	my $genestring = shift;

	# Run experiment
	my $fullname="$name-$id";
	$id++;
	my $cmd = "sh experiments/experiment2 $fullname $lang $size -train.iterations1=0 -train.iterations2=10 -eval.traincorpus=false -genes=$genestring";
	system($cmd);

	# Find scores
	my $incscore = `grep "Labeled *attachment score" experiments/results/*$fullname.ip.eval | sed -e 's/^.* = //g' -e 's/ %\$//g'`;
	my $mstscore = `grep "Labeled *attachment score" experiments/results/*$fullname.mst.eval | sed -e 's/^.* = //g' -e 's/ %\$//g'`;
	chomp($incscore);
	chomp($mstscore);
	print $incscore $mstscore;
	return $incscore;
}

sub multiscores {
	my $genepool = shift;

	# Start threads in background
	my $names = [];
	my $scores = [];
	for (my $i = 0; $i < scalar(@$genepool); ++$i) {
		$id++;
		my $fullname = $names->[$i] = "$name-$id";
		my $cmd = "sh experiments/experiment2 $fullname $lang $size -train.iterations1=0 -train.iterations2=10 -eval.traincorpus=false -genes=$genestring &";
		system($cmd);
		$scores->[$i] = undef;
	}

	# Collect scores
	my $done = 0;
	while (! $done) {
		$done = 1;
		sleep 30;
		for (my $i = 0; $i < scalar(@$names); ++$i) {
			if (! defined($scores->[$i])) {
				my $fullname = $names->[$i];
				my $incscore = `grep "Labeled *attachment score" experiments/results/*$fullname.ip.eval | sed -e 's/^.* = //g' -e 's/ %\$//g'`;
				my $mstscore = `grep "Labeled *attachment score" experiments/results/*$fullname.mst.eval | sed -e 's/^.* = //g' -e 's/ %\$//g'`;
				chomp($incscore);
				chomp($mstscore);
				if (defined($incscore) && $incscore !~ /^\s*$/) {
					$scores->[$i] = $incscore;
				} else {
					$done = 0;
				}
			}
		}
	}

	# Find scores
	return $scores;
}
